package cn.walls1717.juc.rwlock;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * 邮戳锁 <br>
 *
 * 这是对rwl的优化，使用了乐观锁，在一个线程获取到读锁时，另一个线程来获取写锁不会出现阻塞。<br>
 * 不支持重入<br>
 * 不支持条件变量condition<br>
 * 使用过程中不能中断(interrupt())，会影响性能。<br>
 *
 * @author walls1717
 * @create 2022/12/22 13:34
 **/
@Slf4j
public class StampedLockDemo {

    static int num = 27;
    static StampedLock stampedLock = new StampedLock();

    public void write() {
        long stamp = stampedLock.writeLock();
        log.info("写线程准备修改");
        try {
            num = num + 10;
        } finally {
            stampedLock.unlockWrite(stamp);
        }
        log.info("写线程结束修改");
    }

    /**
     * 这样写效果与rwl一样，都是悲观读，读的过程中写操作会被阻塞。
     */
    public void read() {
        long stamp = stampedLock.readLock();
        log.info("read");

        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            int result = num;
            log.info("获取值 " + result);
        } finally {
            stampedLock.unlockRead(stamp);
        }
    }

    /**
     * 乐观读写法
     */
    public void optimisticRead() {
        long stamp = stampedLock.tryOptimisticRead();
        int result = num;
        // stampedLock.validate()返回值为true则表示数据没有被修改过，false表示数据被修改过。
        log.info("4s before " + stampedLock.validate(stamp));
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("4s after " + stampedLock.validate(stamp));

        if (!stampedLock.validate(stamp)) {
            log.info("数据已经被修改");
            long s = stampedLock.readLock();
            try {
                log.info("从乐观读升级为悲观读");
                result = num;
                log.info("悲观读result " + result);
            } finally {
                stampedLock.unlockRead(s);
            }
        }
        log.info("result is " + result);
    }

    public static void main(String[] args) throws InterruptedException {
        StampedLockDemo resource = new StampedLockDemo();

        new Thread(resource::optimisticRead).start();

        TimeUnit.SECONDS.sleep(2);

        new Thread(resource::write).start();

        TimeUnit.SECONDS.sleep(4);

        log.info("result " + num);

    }
}
